package com.fancy.recommendation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fancy.recommendation.dto.request.RankingRequestDTO;
import com.fancy.recommendation.dto.response.RankingResponseVO;
import com.fancy.recommendation.entity.HotRanking;
import com.fancy.recommendation.mapper.HotRankingMapper;
import com.fancy.recommendation.mapper.UserBehaviorMapper;
import com.fancy.recommendation.service.RankingService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 榜单服务实现类
 *
 * @author fancy
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RankingServiceImpl implements RankingService {

    private final HotRankingMapper hotRankingMapper;
    private final UserBehaviorMapper userBehaviorMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final ObjectMapper objectMapper;

    @Override
    public RankingResponseVO getRanking(RankingRequestDTO request) {
        LocalDate periodDate = request.getDate() != null ?
            LocalDate.parse(request.getDate()) : getCurrentPeriodDate(request.getPeriodType());

        String cacheKey = String.format("ranking:%s:%s:%s",
            request.getRankingType(), request.getPeriodType(), periodDate);

        try {
            // 尝试从缓存获取
            String cached = stringRedisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                RankingResponseVO cachedResult = objectMapper.readValue(cached, RankingResponseVO.class);
                log.info("从缓存获取榜单: type={}, period={}", request.getRankingType(), request.getPeriodType());
                return cachedResult;
            }
        } catch (Exception e) {
            log.warn("获取缓存榜单失败", e);
        }

        // 从数据库获取榜单
        List<HotRanking> rankings = hotRankingMapper.getRankingData(
            request.getRankingType(), request.getPeriodType(), periodDate, request.getLimit());

        RankingResponseVO result = buildRankingResponse(rankings, request.getRankingType(),
            request.getPeriodType(), periodDate);

        // 缓存榜单结果
        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(result),
                12, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("缓存榜单失败", e);
        }

        return result;
    }

    @Override
    public List<Map<String, Object>> getRankingTypes() {
        String cacheKey = "ranking:types";

        try {
            String cached = stringRedisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                return objectMapper.readValue(cached, List.class);
            }
        } catch (Exception e) {
            log.warn("获取缓存榜单类型失败", e);
        }

        List<Map<String, Object>> types = hotRankingMapper.getRankingTypes();

        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(types),
                1, TimeUnit.DAYS);
        } catch (Exception e) {
            log.warn("缓存榜单类型失败", e);
        }

        return types;
    }

    @Override
    public List<RankingResponseVO> getRankingHistory(String rankingType, String periodType, Integer limit) {
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(limit * getPeriodDays(periodType));

        List<HotRanking> rankings = hotRankingMapper.getRankingHistory(rankingType, periodType, startDate, endDate);

        // 按日期分组
        Map<LocalDate, List<HotRanking>> groupedRankings = rankings.stream()
            .collect(Collectors.groupingBy(HotRanking::getPeriodDate));

        return groupedRankings.entrySet().stream()
            .sorted(Map.Entry.<LocalDate, List<HotRanking>>comparingByKey().reversed())
            .limit(limit)
            .map(entry -> buildRankingResponse(entry.getValue(), rankingType, periodType, entry.getKey()))
            .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void generateHotSongsRanking(String periodType, LocalDate periodDate) {
        log.info("生成热门歌曲榜单: periodType={}, periodDate={}", periodType, periodDate);

        // 获取时间范围
        LocalDateTime startTime = getStartTime(periodType, periodDate);
        LocalDateTime endTime = startTime.plusDays(getPeriodDays(periodType));

        // 获取热门歌曲数据
        List<Map<String, Object>> hotSongs = userBehaviorMapper.getHotContentByAction("play", startTime, 100);

        // 过滤歌曲类型
        List<Map<String, Object>> songData = hotSongs.stream()
            .filter(item -> "song".equals(item.get("target_type")))
            .collect(Collectors.toList());

        // 生成榜单数据
        List<HotRanking> rankings = new ArrayList<>();
        for (int i = 0; i < Math.min(songData.size(), 50); i++) {
            Map<String, Object> song = songData.get(i);

            HotRanking ranking = new HotRanking();
            ranking.setRankingType("hot_songs");
            ranking.setTargetId(((Number) song.get("target_id")).longValue());
            ranking.setTargetType("song");
            ranking.setRankingPosition(i + 1);
            ranking.setScore(((Number) song.get("total_score")).longValue());
            ranking.setPeriodType(periodType);
            ranking.setPeriodDate(periodDate);
            ranking.setCreateTime(LocalDateTime.now());

            rankings.add(ranking);
        }

        // 删除旧榜单
        hotRankingMapper.deleteRankingByTypeAndDate("hot_songs", periodType, periodDate);

        // 插入新榜单
        if (!rankings.isEmpty()) {
            hotRankingMapper.batchInsertRanking(rankings);
            log.info("生成热门歌曲榜单完成: 条数={}", rankings.size());
        }

        // 清除相关缓存
        clearRankingCache("hot_songs", periodType);
    }

    @Override
    @Transactional
    public void generateHotAlbumsRanking(String periodType, LocalDate periodDate) {
        log.info("生成热门专辑榜单: periodType={}, periodDate={}", periodType, periodDate);

        LocalDateTime startTime = getStartTime(periodType, periodDate);

        // 获取热门专辑数据（基于收藏行为）
        List<Map<String, Object>> hotAlbums = userBehaviorMapper.getHotContentByAction("collect", startTime, 100);

        List<Map<String, Object>> albumData = hotAlbums.stream()
            .filter(item -> "album".equals(item.get("target_type")))
            .collect(Collectors.toList());

        List<HotRanking> rankings = new ArrayList<>();
        for (int i = 0; i < Math.min(albumData.size(), 50); i++) {
            Map<String, Object> album = albumData.get(i);

            HotRanking ranking = new HotRanking();
            ranking.setRankingType("hot_albums");
            ranking.setTargetId(((Number) album.get("target_id")).longValue());
            ranking.setTargetType("album");
            ranking.setRankingPosition(i + 1);
            ranking.setScore(((Number) album.get("total_score")).longValue());
            ranking.setPeriodType(periodType);
            ranking.setPeriodDate(periodDate);
            ranking.setCreateTime(LocalDateTime.now());

            rankings.add(ranking);
        }

        hotRankingMapper.deleteRankingByTypeAndDate("hot_albums", periodType, periodDate);

        if (!rankings.isEmpty()) {
            hotRankingMapper.batchInsertRanking(rankings);
            log.info("生成热门专辑榜单完成: 条数={}", rankings.size());
        }

        clearRankingCache("hot_albums", periodType);
    }

    @Override
    @Transactional
    public void generateNewSongsRanking(String periodType, LocalDate periodDate) {
        log.info("生成新歌榜单: periodType={}, periodDate={}", periodType, periodDate);

        LocalDateTime startTime = getStartTime(periodType, periodDate);

        // 获取新歌数据（最近发布且有播放量的歌曲）
        List<Map<String, Object>> newSongs = userBehaviorMapper.getHotContentByAction("play",
            startTime.minusDays(30), 100);

        // 这里应该结合音乐服务的发布时间进行过滤，暂时用播放数据模拟
        List<Map<String, Object>> songData = newSongs.stream()
            .filter(item -> "song".equals(item.get("target_type")))
            .collect(Collectors.toList());

        List<HotRanking> rankings = new ArrayList<>();
        for (int i = 0; i < Math.min(songData.size(), 50); i++) {
            Map<String, Object> song = songData.get(i);

            HotRanking ranking = new HotRanking();
            ranking.setRankingType("new_songs");
            ranking.setTargetId(((Number) song.get("target_id")).longValue());
            ranking.setTargetType("song");
            ranking.setRankingPosition(i + 1);
            ranking.setScore(((Number) song.get("total_score")).longValue());
            ranking.setPeriodType(periodType);
            ranking.setPeriodDate(periodDate);
            ranking.setCreateTime(LocalDateTime.now());

            rankings.add(ranking);
        }

        hotRankingMapper.deleteRankingByTypeAndDate("new_songs", periodType, periodDate);

        if (!rankings.isEmpty()) {
            hotRankingMapper.batchInsertRanking(rankings);
            log.info("生成新歌榜单完成: 条数={}", rankings.size());
        }

        clearRankingCache("new_songs", periodType);
    }

    @Override
    @Transactional
    public void batchUpdateRanking(List<HotRanking> rankings) {
        if (rankings == null || rankings.isEmpty()) {
            return;
        }

        hotRankingMapper.batchInsertRanking(rankings);
        log.info("批量更新榜单数据: 条数={}", rankings.size());

        // 清除相关缓存
        Set<String> cacheKeys = rankings.stream()
            .map(r -> String.format("ranking:%s:%s:*", r.getRankingType(), r.getPeriodType()))
            .collect(Collectors.toSet());

        for (String pattern : cacheKeys) {
            Set<String> keys = stringRedisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                stringRedisTemplate.delete(keys);
            }
        }
    }

    @Override
    @Transactional
    public void cleanHistoryRanking(Integer keepDays) {
        if (keepDays == null || keepDays <= 0) {
            keepDays = 90; // 默认保留90天
        }

        LocalDate beforeDate = LocalDate.now().minusDays(keepDays);
        int deletedCount = hotRankingMapper.deleteHistoryRanking(beforeDate);

        log.info("清理历史榜单数据完成: 清理了{}条记录", deletedCount);
    }

    @Override
    public List<Map<String, Object>> getRealtimeHotContent(String contentType, Integer limit) {
        if (limit == null || limit <= 0) {
            limit = 20;
        }

        String cacheKey = "realtime:hot:" + contentType;

        try {
            String cached = stringRedisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                return objectMapper.readValue(cached, List.class);
            }
        } catch (Exception e) {
            log.warn("获取实时热门缓存失败", e);
        }

        // 获取最近1小时的热门数据
        LocalDateTime startTime = LocalDateTime.now().minusHours(1);
        List<Map<String, Object>> hotContent = userBehaviorMapper.getHotContentByAction("play", startTime, limit);

        List<Map<String, Object>> result = hotContent.stream()
            .filter(item -> contentType.equals(item.get("target_type")))
            .limit(limit)
            .collect(Collectors.toList());

        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(result),
                5, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.warn("缓存实时热门数据失败", e);
        }

        return result;
    }

    @Override
    public Map<String, Object> getRankingStatistics(String rankingType, String periodType) {
        String cacheKey = String.format("ranking:stats:%s:%s", rankingType, periodType);

        try {
            String cached = stringRedisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                return objectMapper.readValue(cached, Map.class);
            }
        } catch (Exception e) {
            log.warn("获取榜单统计缓存失败", e);
        }

        Map<String, Object> stats = new HashMap<>();

        // 获取最新榜单
        List<HotRanking> latestRanking = hotRankingMapper.getLatestRanking(rankingType, periodType, 50);

        if (!latestRanking.isEmpty()) {
            stats.put("totalItems", latestRanking.size());
            stats.put("topScore", latestRanking.get(0).getScore());
            stats.put("lastUpdate", latestRanking.get(0).getCreateTime());
            stats.put("periodDate", latestRanking.get(0).getPeriodDate());

            // 计算平均分数
            double avgScore = latestRanking.stream()
                .mapToLong(HotRanking::getScore)
                .average()
                .orElse(0.0);
            stats.put("avgScore", avgScore);
        } else {
            stats.put("totalItems", 0);
            stats.put("topScore", 0);
            stats.put("avgScore", 0.0);
        }

        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(stats),
                1, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("缓存榜单统计失败", e);
        }

        return stats;
    }

    /**
     * 定时生成榜单 - 每天凌晨1点执行
     */
    @Scheduled(cron = "0 0 1 * * ?")
    @Transactional
    public void generateDailyRankings() {
        log.info("开始生成每日榜单");

        LocalDate yesterday = LocalDate.now().minusDays(1);

        try {
            generateHotSongsRanking("daily", yesterday);
            generateHotAlbumsRanking("daily", yesterday);
            generateNewSongsRanking("daily", yesterday);

            log.info("每日榜单生成完成");
        } catch (Exception e) {
            log.error("生成每日榜单失败", e);
        }
    }

    /**
     * 定时生成周榜 - 每周一凌晨2点执行
     */
    @Scheduled(cron = "0 0 2 ? * MON")
    @Transactional
    public void generateWeeklyRankings() {
        log.info("开始生成每周榜单");

        LocalDate lastWeek = LocalDate.now().minusWeeks(1);

        try {
            generateHotSongsRanking("weekly", lastWeek);
            generateHotAlbumsRanking("weekly", lastWeek);
            generateNewSongsRanking("weekly", lastWeek);

            log.info("每周榜单生成完成");
        } catch (Exception e) {
            log.error("生成每周榜单失败", e);
        }
    }

    /**
     * 定时生成月榜 - 每月1号凌晨3点执行
     */
    @Scheduled(cron = "0 0 3 1 * ?")
    @Transactional
    public void generateMonthlyRankings() {
        log.info("开始生成每月榜单");

        LocalDate lastMonth = LocalDate.now().minusMonths(1);

        try {
            generateHotSongsRanking("monthly", lastMonth);
            generateHotAlbumsRanking("monthly", lastMonth);
            generateNewSongsRanking("monthly", lastMonth);

            log.info("每月榜单生成完成");
        } catch (Exception e) {
            log.error("生成每月榜单失败", e);
        }
    }

    /**
     * 定时清理历史榜单 - 每天凌晨4点执行
     */
    @Scheduled(cron = "0 0 4 * * ?")
    public void cleanupHistoryRankings() {
        log.info("开始清理历史榜单数据");

        try {
            cleanHistoryRanking(90); // 保留90天
            log.info("历史榜单数据清理完成");
        } catch (Exception e) {
            log.error("清理历史榜单数据失败", e);
        }
    }

    // 私有辅助方法

    private RankingResponseVO buildRankingResponse(List<HotRanking> rankings, String rankingType,
                                                 String periodType, LocalDate periodDate) {
        RankingResponseVO response = new RankingResponseVO();
        response.setRankingType(rankingType);
        response.setPeriodType(periodType);
        response.setPeriodDate(periodDate);
        response.setUpdateTime(LocalDate.now());

        List<RankingResponseVO.RankingItemVO> items = rankings.stream()
            .map(this::convertToItemVO)
            .collect(Collectors.toList());

        response.setItems(items);
        return response;
    }

    private RankingResponseVO.RankingItemVO convertToItemVO(HotRanking ranking) {
        RankingResponseVO.RankingItemVO item = new RankingResponseVO.RankingItemVO();
        item.setPosition(ranking.getRankingPosition());
        item.setTargetId(ranking.getTargetId());
        item.setTargetType(ranking.getTargetType());
        item.setScore(ranking.getScore());
        // 这里可以通过Feign调用其他服务获取详细信息
        // item.setTargetInfo(getTargetInfo(ranking.getTargetType(), ranking.getTargetId()));
        return item;
    }

    private LocalDate getCurrentPeriodDate(String periodType) {
        LocalDate now = LocalDate.now();
        switch (periodType.toLowerCase()) {
            case "daily":
                return now.minusDays(1);
            case "weekly":
                return now.minusWeeks(1);
            case "monthly":
                return now.minusMonths(1);
            default:
                return now.minusDays(1);
        }
    }

    private LocalDateTime getStartTime(String periodType, LocalDate periodDate) {
        return periodDate.atStartOfDay();
    }

    private int getPeriodDays(String periodType) {
        switch (periodType.toLowerCase()) {
            case "daily":
                return 1;
            case "weekly":
                return 7;
            case "monthly":
                return 30;
            default:
                return 1;
        }
    }

    private void clearRankingCache(String rankingType, String periodType) {
        String pattern = String.format("ranking:%s:%s:*", rankingType, periodType);
        Set<String> keys = stringRedisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            stringRedisTemplate.delete(keys);
            log.info("清除榜单缓存: pattern={}, count={}", pattern, keys.size());
        }
    }
}
